package br.com.edoc.gerentes;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.Transient;

import ognl.Ognl;
import ognl.OgnlException;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import br.com.edoc.entidade.EntidadePadrao;
import br.com.edoc.excecoes.ExceptionApplication;
import br.com.edoc.gerentes.GerenteHQL;
import br.com.edoc.hibernate.HibernateUtilitario;

public class GerenteHQLImpl implements GerenteHQL {
	
	private HibernateUtilitario hib;
	private Session session;
	private Transaction transacao;
	
	@SuppressWarnings("rawtypes")
	private String getEndreco(Class classe){
		return  classe.getName().substring(classe.getPackage().getName().length()+1)  ;
	}
	
	@SuppressWarnings("rawtypes")
	private String getNameClass(Class classe){
		return  classe.getName().substring(classe.getPackage().getName().length()+1)  ;
	}
	
	@SuppressWarnings("rawtypes")
	public void getFieldObject(Class classe,List<Field> lista){	
		if(classe.getSuperclass()==null)
			return;
		if((classe.getSuperclass() !=null)){
			getFieldObject(classe.getSuperclass(), lista);
		}
		Field[] field = classe.getDeclaredFields();
		lista.addAll(Arrays.asList(field));
	}
	@SuppressWarnings("rawtypes")
	public String gerarHQLForObject(Class classe,Object obj) throws ExceptionApplication{
		try { 

			String sql = "from "+getEndreco(classe)+" obj ";
			Field m[]=null;
			if(classe.getSuperclass().getSuperclass()==null)
				m = classe.getDeclaredFields();
			else{
				List<Field> lista = new ArrayList<Field>();
				getFieldObject(classe, lista);
				m =  lista.toArray(new Field[lista.size()]);
			}
			
			getFieldObject(classe, new ArrayList<Field>());
			int b = 0;

			for (int i = 0; i < m.length; i++) {   
				if(!m[i].getName().equals("serialVersionUID") && m[i].getAnnotation(Transient.class)==null){					
					if(!m[i].getName().equals("id") && ( Ognl.getValue(m[i].getName(), m[i].getDeclaringClass().cast(obj))!= null )&&(!getNameClass(m[i].getType()).equals("List"))){
						String condicao = "";
						if(!getNameClass(m[i].getType()).equals("Boolean")){
							if(b==0){
								condicao+=" where ";
							}else{
								condicao+=" and ";
							}
						}
						if(!getNameClass(m[i].getType()).equals("Boolean")  && !getNameClass(m[i].getType()).equals("String")){
							condicao+= " obj."+m[i].getName()+" ";
						}
						else if(getNameClass(m[i].getType()).equals("String")){
							condicao+= " lower(TRANSLATE(obj."+m[i].getName()+",'ÀÁáàÉÈÊéèêÍíÓóÒòÚúçÇãÃÕ','AAaaEEEeeeIiOoOoUucCaAO')) ";
						}
						if(getNameClass(m[i].getType()).equals("String")){
							condicao+= "like lower(TRANSLATE(:"+m[i].getName()+",'ÀÁáàÉÈÊéèêÍíÓóÒòÚúçÇãÃÕ','AAaaEEEeeeIiOoOoUucCaAO') )";
						}
						else if( Ognl.getValue(m[i].getName(),obj) instanceof EntidadePadrao){
							EntidadePadrao entidade = (EntidadePadrao) Ognl.getValue(m[i].getName(), obj);
							if(entidade.getId()==null){
								condicao = "";
								b--;
							}else{
								condicao+= "= :"+m[i].getName()+"  ";
							}
						}
						else if(!getNameClass(m[i].getType()).equals("Boolean")){
							condicao+= "= :"+m[i].getName()+"  ";
						}
						if(!getNameClass(m[i].getType()).equals("Boolean"))
							b++;
						
						sql+=condicao;
					}

				}
			}
			return sql;
		} catch (OgnlException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		}

	}
	
	
	public <T extends EntidadePadrao> List<T> getQuery(Class<T> classe, Object objeto,Integer quantMaxResult) throws ExceptionApplication{

		try{ 
			Query query = getSession().createQuery(gerarHQLForObject(classe,objeto));
			query.setMaxResults(quantMaxResult);
			Field m[]=null;
			if(classe.getSuperclass().getSuperclass()==null)
				m = classe.getDeclaredFields();
			else{
				List<Field> lista = new ArrayList<Field>();
				getFieldObject(classe, lista);
				m =  lista.toArray(new Field[lista.size()]);
			}
			for (int i = 0; i < m.length; i++) {  
				if(!m[i].getName().equals("serialVersionUID") && m[i].getAnnotation(Transient.class)==null){
					if(!m[i].getName().equals("id") && ( Ognl.getValue(m[i].getName(), objeto)!= null ) && (!getNameClass(m[i].getType()).equals("List"))){

						Object valor = Ognl.getValue(m[i].getName(), objeto);
						if(getNameClass(m[i].getType()).equals("String")){
							if(valor==null){
								valor = "";
							}
							query.setString(m[i].getName(), "%"+((String) valor)+"%");
						}else
							if(getNameClass(m[i].getType()).equals("Integer")){
								query.setInteger(m[i].getName(), ((Integer) valor));
							}else 
								if(getNameClass(m[i].getType()).equals("Float")){
									query.setFloat(m[i].getName(), ((Integer) valor));
								}else
									if(getNameClass(m[i].getType()).equals("Date")){
										query.setTimestamp(m[i].getName(), ((Date) valor));
									}

									else
										if( (Ognl.getValue(m[i].getName(),objeto) instanceof EntidadePadrao)&&  ((Ognl.getValue(m[i].getName(),objeto))!=null)){
											EntidadePadrao entidade = (EntidadePadrao) Ognl.getValue(m[i].getName(), objeto);
											if(entidade.getId()!=null){
												query.setEntity(m[i].getName(), entidade);
											}
										}

										else if(!getNameClass(m[i].getType()).equals("Boolean")){
											query.setEntity(m[i].getName(), objeto);
										}

					}
				}
			}
			return listAndCast(query);
		}catch (OgnlException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		} catch (ExceptionApplication e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		}

	}
	
	
	public <T extends EntidadePadrao>  List<T> getQuery(Class<T> classe, Object objeto) throws ExceptionApplication{

		try{ 
			Query query = getSession().createQuery(gerarHQLForObject(classe,objeto));
			Field m[]=null;
			if(classe.getSuperclass().getSuperclass()==null)
				m = classe.getDeclaredFields();
			else{
				List<Field> lista = new ArrayList<Field>();
				getFieldObject(classe, lista);
				m =  lista.toArray(new Field[lista.size()]);
			}
			for (int i = 0; i < m.length; i++) {  
				if(!m[i].getName().equals("serialVersionUID") && m[i].getAnnotation(Transient.class)==null){
					if(!m[i].getName().equals("id") && ( Ognl.getValue(m[i].getName(), objeto)!= null ) && (!getNameClass(m[i].getType()).equals("List"))){

						Object valor = Ognl.getValue(m[i].getName(), objeto);
						
						if(getNameClass(m[i].getType()).equals("String")){
							if(valor==null){
								valor = "";
							}
							query.setString(m[i].getName(), "%"+((String) valor)+"%");
						}else
							if(getNameClass(m[i].getType()).equals("Integer")){
								query.setInteger(m[i].getName(), ((Integer) valor));
							}else 
								if(getNameClass(m[i].getType()).equals("Float")){
									query.setFloat(m[i].getName(), ((Integer) valor));
								}else
									
									if(getNameClass(m[i].getType()).equals("Date")){
										query.setTimestamp(m[i].getName(), ((Date) valor));
									}

									else
										if( (Ognl.getValue(m[i].getName(),objeto) instanceof EntidadePadrao)&&  ((Ognl.getValue(m[i].getName(),objeto))!=null)){
											EntidadePadrao entidade = (EntidadePadrao) Ognl.getValue(m[i].getName(), objeto);
											if(entidade.getId()!=null){
												query.setEntity(m[i].getName(), entidade);
											}
										}else										
										if(getNameClass(m[i].getType().getSuperclass()).equals("Enum")){
											query.setInteger(m[i].getName(), ((java.lang.Enum)Ognl.getValue(m[i].getName(), objeto)).ordinal());
										}
										else if(!getNameClass(m[i].getType()).equals("Boolean")){
											query.setEntity(m[i].getName(), objeto);
										}

					}
				}
			}
			return listAndCast(query);
		}catch (OgnlException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		} catch (HibernateException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		} catch (ExceptionApplication e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		}
	}
	
	
	public <T extends EntidadePadrao> List<T> getQuery(Class<T> classe, Object objeto,String ordenador) throws ExceptionApplication {

		try{ 
			String hql = gerarHQLForObject(classe,objeto);
			hql+="order by "+ordenador;
			Query query = getSession().createQuery(hql);
			Field m[]=null;
			if(classe.getSuperclass().getSuperclass()==null)
				m = classe.getDeclaredFields();
			else{
				List<Field> lista = new ArrayList<Field>();
				getFieldObject(classe, lista);
				m =  lista.toArray(new Field[lista.size()]);
			}
			for (int i = 0; i < m.length; i++) {
				if(!m[i].getName().equals("serialVersionUID") && m[i].getAnnotation(Transient.class)==null){
					if(!m[i].getName().equals("id") && ( Ognl.getValue(m[i].getName(), objeto)!= null ) && (!getNameClass(m[i].getType()).equals("List"))){

						Object valor = Ognl.getValue(m[i].getName(), objeto);
						if(getNameClass(m[i].getType()).equals("String")){
							if(valor==null){
								valor = "";
							}
							query.setString(m[i].getName(), "%"+((String) valor)+"%");
						}else
							if(getNameClass(m[i].getType()).equals("Integer")){
								query.setInteger(m[i].getName(), ((Integer) valor));
							}else 
								if(getNameClass(m[i].getType()).equals("Float")){
									query.setFloat(m[i].getName(), ((Integer) valor));
								}else
									if(getNameClass(m[i].getType()).equals("Date")){
										query.setTimestamp(m[i].getName(), ((Date) valor));
									}

									else
										if( (Ognl.getValue(m[i].getName(),objeto) instanceof EntidadePadrao)&&  ((Ognl.getValue(m[i].getName(),objeto))!=null)){
											EntidadePadrao entidade = (EntidadePadrao) Ognl.getValue(m[i].getName(), objeto);
											if(entidade.getId()!=null){
												query.setEntity(m[i].getName(), entidade);
											}
										}

										else if(!getNameClass(m[i].getType()).equals("Boolean")){
											query.setEntity(m[i].getName(), objeto);
										}

					}
				}
			}
			return listAndCast(query);
		}catch (OgnlException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		}
	}
	
	
	public <T extends EntidadePadrao> List<T> getQuery(Class<T> classe, Object objeto,String ordenador,Integer quantMaxResult) throws ExceptionApplication{

		try{ 
			String hql = gerarHQLForObject(classe,objeto);
			hql+="order by "+ordenador;
			Query query = getSession().createQuery(hql);
			
			Field m[]=null;
			if(classe.getSuperclass().getSuperclass()==null)
				m = classe.getDeclaredFields();
			else{
				List<Field> lista = new ArrayList<Field>();
				getFieldObject(classe, lista);
				m =  lista.toArray(new Field[lista.size()]);
			}
			for (int i = 0; i < m.length; i++) {
				if(!m[i].getName().equals("serialVersionUID") && m[i].getAnnotation(Transient.class)==null){
					if(!m[i].getName().equals("id") && ( Ognl.getValue(m[i].getName(), objeto)!= null ) && (!getNameClass(m[i].getType()).equals("List"))){

						Object valor = Ognl.getValue(m[i].getName(), objeto);
						if(getNameClass(m[i].getType()).equals("String")){
							if(valor==null){
								valor = "";
							}
							query.setString(m[i].getName(), "%"+((String) valor)+"%");
						}else
							if(getNameClass(m[i].getType()).equals("Integer")){
								query.setInteger(m[i].getName(), ((Integer) valor));
							}else 
								if(getNameClass(m[i].getType()).equals("Float")){
									query.setFloat(m[i].getName(), ((Integer) valor));
								}else
									if(getNameClass(m[i].getType()).equals("Date")){
										query.setTimestamp(m[i].getName(), ((Date) valor));
									}

									else
										if( (Ognl.getValue(m[i].getName(),objeto) instanceof EntidadePadrao)&&  ((Ognl.getValue(m[i].getName(),objeto))!=null)){
											EntidadePadrao entidade = (EntidadePadrao) Ognl.getValue(m[i].getName(), objeto);
											if(entidade.getId()!=null){
												query.setEntity(m[i].getName(), entidade);
											}
										}

										else if(!getNameClass(m[i].getType()).equals("Boolean")){
											query.setEntity(m[i].getName(), objeto);
										}

					}
				}
			}
			;
			return listAndCast(query.setMaxResults(quantMaxResult));
		}catch (OgnlException e) {
			// TODO Auto-generated catch block
			throw new ExceptionApplication(e.getMessage());
		}
	}
	
	public HibernateUtilitario getHib() {
		return hib;
	}
	
	public void setHib(HibernateUtilitario hib) {
		this.hib = hib;
	}
	
	@SuppressWarnings("static-access")
	public Session getSession() {
		if(session ==null  || !session.isOpen()){
			setSession(hib.currentSession());
		}
		return session;
	}
	
	public void setSession(Session session) {
		this.session = session;
	}
	
	public Transaction getTransacao() {
		setTransacao(getSession().beginTransaction());
		return transacao;
	}
	
	public void setTransacao(Transaction transacao) {
		this.transacao = transacao;
	}
	
	public void comit(){
		getTransacao().commit();
	}
	
	public void rowback(){
		getTransacao().rollback();
	}
	
	public void close(){
		getSession().close();
	}
	public static <T> List<T> listAndCast(Query q) {
		@SuppressWarnings("unchecked")
		List<T> list = q.list();
		return list;
	}
	
}